#include "fileparser.h"
#include "algorithms.h"

#include "adjacencestructgraph.h"
#include "altadjacencestructgraph.h"
#include "altsetgraph.h"
#include "incidencematrixgraph.h"
#include "setgraph.h"
#include "adjacencematrix.h"
#include "incidencematrixgraph.h"

#include <fstream>
#include <iostream>
#include <chrono>

using namespace std;
using namespace std::chrono;

template<typename G>
void fillGraph(G &graph, const Parser &parser)
{  
  vector<Parser::Pair> edges = parser.getEdges();

  int nNodes = parser.getNumberOfNodes();

  for (int i = 0; i< nNodes; i++) {
    graph.addNode();
  }

  for (Parser::Pair edge: edges) {
    graph.addEdge(edge.node1, edge.node2);
  }  
}

template<typename G>
double searchArticulations(G &graph)
{
  high_resolution_clock::time_point before = high_resolution_clock::now();

  vector<int> articulations = ArticulationSearch<G>(graph).getArticulations();

  high_resolution_clock::time_point after = high_resolution_clock::now();
  duration<double> d = duration_cast<chrono::duration<double>>(after - before);
  return d.count();
}

template<typename G>
double searchBFS(G &graph)
{
  high_resolution_clock::time_point before = high_resolution_clock::now();

  vector<int> order = bfs(graph);

  high_resolution_clock::time_point after = high_resolution_clock::now();
  duration<double> d = duration_cast<chrono::duration<double>>(after - before);
  return d.count();
}

void executeArticulationSearchs(const Parser &parser, ostream &out)
{
  {
    AdjacenceStructGraph<> graph;
    fillGraph(graph,parser);
    double time = searchArticulations(graph);
    out << time << "; "; 
  }

  {
    AltAdjacenceStructGraph<> graph;
    fillGraph(graph,parser);
    double time = searchArticulations(graph);  
    out << time << "; "; 
  }

  {
    SetGraph<> graph;
    fillGraph(graph,parser);
    double time = searchArticulations(graph);  
    out << time << "; "; 
  }

  {
    AltSetGraph<> graph;
    fillGraph(graph,parser);
    double time = searchArticulations(graph);  
    out << time << "; "; 
  }

  {
    AdjacenceMatrixGraph<> graph(parser.getNumberOfNodes());
    fillGraph(graph,parser);
    double time = searchArticulations(graph);  
    out << time << "; "; 
  }
 
  {
    IncidenceMatrixGraph<> graph(parser.getNumberOfNodes());
    fillGraph(graph,parser);
    double time = searchArticulations(graph);  
    out << time << endl; 
  }
}

template<typename G>
double verifyAdjacences(G &graph)
{
  high_resolution_clock::time_point before = high_resolution_clock::now();

  int countNodes = graph.countNodes();

  // para todo vértice, testa se ele é adjacente a todos os outros
  for (int i = 0; i < countNodes; i++) {
    for (int j = 0; j < countNodes; j++) {
      graph.areNodesAdjacents(i,j);
    }
  }

  high_resolution_clock::time_point after = high_resolution_clock::now();
  duration<double> d = duration_cast<chrono::duration<double>>(after - before);
  return d.count();
}

void executeBFS(const Parser &parser, ostream &out)
{
  {
    AdjacenceStructGraph<> graph;
    fillGraph(graph,parser);
    double time = searchBFS(graph);
    out << time << "; "; 
  }

  {
    AltAdjacenceStructGraph<> graph;
    fillGraph(graph,parser);
    double time = searchBFS(graph);
    out << time << "; "; 
  }

  {
    SetGraph<> graph;
    fillGraph(graph,parser);
    double time = searchBFS(graph);
    out << time << "; "; 
  }

  {
    AltSetGraph<> graph;
    fillGraph(graph,parser);
    double time = searchBFS(graph);
    out << time << "; "; 
  }

  {
    AdjacenceMatrixGraph<> graph(parser.getNumberOfNodes());
    fillGraph(graph,parser);
    double time = searchBFS(graph);
    out << time << "; "; 
  }
 
  {
    IncidenceMatrixGraph<> graph(parser.getNumberOfNodes());
    fillGraph(graph,parser);
    double time = searchBFS(graph);
    out << time << endl; 
  }
}

void executeAdjacenceVerifications(const Parser &parser, ostream &out)
{
  {
    AdjacenceStructGraph<> graph;
    fillGraph(graph,parser);
    double time = verifyAdjacences(graph);  
    out << time << "; "; 
  }

  {
    AltAdjacenceStructGraph<> graph;
    fillGraph(graph,parser);
    double time = verifyAdjacences(graph);  
    out << time << "; "; 
  }

  {
    SetGraph<> graph;
    fillGraph(graph,parser);
    double time = verifyAdjacences(graph);  
    out << time << "; "; 
  }

  {
    AltSetGraph<> graph;
    fillGraph(graph,parser);
    double time = verifyAdjacences(graph);  
    out << time << "; "; 
  }

  {
    AdjacenceMatrixGraph<> graph(parser.getNumberOfNodes());
    fillGraph(graph,parser);
    double time = verifyAdjacences(graph);  
    out << time << endl; 
  }
}


int main(int argc, char **argv)
{
  if (argc < 2) {
    cerr << "Uso: " << argv[0] << " arquivo.txt" << endl;
    return 1;
  }

  ifstream input(argv[1]);
 
  if (!input) {
    cerr << "Erro ao abrir o arquivo." << endl;
    return 1;
  }

  Parser parser(input);

  parser.parse();

  /*{
    ofstream out("artitulationOut.csv", std::ios::app);
    out << parser.getNumberOfNodes() << "; " <<  parser.getEdges().size() << "; ";
    executeArticulationSearchs(parser,out);
  }

  {
    ofstream out("bfsOut.csv",std::ios::app);
    out << parser.getNumberOfNodes() << "; " <<  parser.getEdges().size() << "; ";
    executeBFS(parser,out);
  }*/

  {
    ofstream out("adjacenceOut.csv",std::ios::app);
    out << parser.getNumberOfNodes() << "; " <<  parser.getEdges().size() << "; ";
    executeAdjacenceVerifications(parser,out);
  }

}

